package br.com.arquitetura.model;

import java.util.ArrayList;
import java.util.List;

public class MemoriaDeControle {
	
	
	public static final int R0  	= Microinstrucao.R0;
	public static final int R1  	= Microinstrucao.R1;
	public static final int R2  	= Microinstrucao.R2;
	public static final int R3  	= Microinstrucao.R3;
	public static final int R4  	= Microinstrucao.R4;
	public static final int RT  	= Microinstrucao.RT;
	public static final int RDADO 	= Microinstrucao.RDADO;
	public static final int PC  	= Microinstrucao.PC;
			
	public static final int IR  	= Microinstrucao.IR;
	public static final int REND  	= Microinstrucao.REND;
	
	public static final int MEM_EN 	= Microinstrucao.MEM_EN;
	public static final int MEM_RW 	= Microinstrucao.MEM_RW;
			
	public static final int MUXA0  	= Microinstrucao.MUXA0;
	public static final int MUXA1  	= Microinstrucao.MUXA1;
	public static final int MUXA2  	= Microinstrucao.MUXA2;
			
	public static final int MUXB0  	= Microinstrucao.MUXB0;
	public static final int MUXB1  	= Microinstrucao.MUXB1;
	public static final int MUXB2  	= Microinstrucao.MUXB2;
	
	public static final int MUX  	= Microinstrucao.MUX;
	
	public static final int ULA0  	= Microinstrucao.ULA0;
	public static final int ULA1  	= Microinstrucao.ULA1;
	public static final int ULA2  	= Microinstrucao.ULA2;
	public static final int ULA3  	= Microinstrucao.ULA3;
	
	private static final int MUXA_RDADO = 0;
	private static final int MUXA_R0 = MUXA0;
	private static final int MUXA_R1 = MUXA1;
	private static final int MUXA_R2 = MUXA1 | MUXA0;
	private static final int MUXA_R3 = MUXA2;
	private static final int MUXA_R4 = MUXA2 | MUXA0;
	private static final int MUXA_RT = MUXA2 | MUXA1;
	private static final int MUXA_PC = MUXA2 | MUXA1 | MUXA0;

	private static final int MUXB_RDADO = 0;
	private static final int MUXB_R0 = MUXB0;
	private static final int MUXB_R1 = MUXB1;
	private static final int MUXB_R2 = MUXB1 | MUXB0;
	private static final int MUXB_R3 = MUXB2;
	private static final int MUXB_R4 = MUXB2 | MUXB0;
	private static final int MUXB_RT = MUXB2 | MUXB1;
	private static final int MUXB_PC = MUXB2 | MUXB1 | MUXB0;
	
	private static final int MUX_MEM = 0;
	private static final int MUX_BARRAMENTO = MUX;
	
	private static final int MEM_R = MEM_EN | MEM_RW;
	private static final int MEM_W = MEM_EN | 0;
	
	private static final int ULA_A 			= 0;					//S = A
	private static final int ULA_INC_A 		= ULA0;					//S = A + 1
	private static final int ULA_ADD_A_B 	= ULA1;					//S = A + B
	private static final int ULA_SUB_A_B 	= ULA1 | ULA0;  		//S = B - A
	private static final int ULA_A_AND_B 	= ULA2 ;				//S = A & B
	private static final int ULA_A_OR_B 	= ULA2 | ULA0;			//S = A | B
	private static final int ULA_NOT_A 		= ULA2 | ULA1;			//S = ~A
	private static final int ULA_CLEAR 		= ULA2 | ULA1 | ULA0;	//S = 0
	private static final int ULA_MENOS_A 	= ULA3;					//S = -A
	private static final int ULA_A_SHL 		= ULA3 | ULA0;			//S = A << 1
	private static final int ULA_A_SHR 		= ULA3 | ULA1;			//S = A >> 1

	
	
	public static final int FIM_DE_PROGRAMA = 0;
	public static final int DECODIFICA_INSTRUCAO = 1;
	public static final int INCREMENTA_PC = 2;
	public static final int BUSCA_INSTRUCAO = 3;
	
	public static final int ADD_CONST_RO = 6;
	public static final int ADD_CONST_R1 = 9;
	public static final int ADD_CONST_R2 = 12;
	public static final int ADD_CONST_R3 = 15;
	public static final int ADD_CONST_R4 = 18;
		
	private static final int ADD_CONST_RI_MEM = 21;
	public static final int ADD_CONST_R0_MEM = 27;
	public static final int ADD_CONST_R1_MEM = 28;
	public static final int ADD_CONST_R2_MEM = 29;
	public static final int ADD_CONST_R3_MEM = 30;
	public static final int ADD_CONST_R4_MEM = 31;
	
	public static final int ADD_R0_R0 = 32;
	public static final int ADD_R1_R0 = 33;
	public static final int ADD_R2_R0 = 34;
	public static final int ADD_R3_R0 = 35;
	public static final int ADD_R4_R0 = 36;
	
	public static final int ADD_R0_R1 = 37;
	public static final int ADD_R1_R1 = 38;
	public static final int ADD_R2_R1 = 39;
	public static final int ADD_R3_R1 = 40;
	public static final int ADD_R4_R1 = 41;
	
	public static final int ADD_R0_R2 = 42;
	public static final int ADD_R1_R2 = 43;
	public static final int ADD_R2_R2 = 44;
	public static final int ADD_R3_R2 = 45;
	public static final int ADD_R4_R2 = 46;
	
	public static final int ADD_R0_R3 = 47;
	public static final int ADD_R1_R3 = 48;
	public static final int ADD_R2_R3 = 49;
	public static final int ADD_R3_R3 = 50;
	public static final int ADD_R4_R3 = 51;
	
	public static final int ADD_R0_R4 = 52;
	public static final int ADD_R1_R4 = 53;
	public static final int ADD_R2_R4 = 54;
	public static final int ADD_R3_R4 = 55;
	public static final int ADD_R4_R4 = 56;
	
	private static final int ADD_RI_RJ_MEM = 57;
	public static final int ADD_R0_R0_MEM = 59;
	public static final int ADD_R1_R0_MEM = 61;
	public static final int ADD_R2_R0_MEM = 63;
	public static final int ADD_R3_R0_MEM = 65;
	public static final int ADD_R4_R0_MEM = 67;
	
	public static final int ADD_R0_R1_MEM = 69;
	public static final int ADD_R1_R1_MEM = 71;
	public static final int ADD_R2_R1_MEM = 73;
	public static final int ADD_R3_R1_MEM = 75;
	public static final int ADD_R4_R1_MEM = 77;
	
	public static final int ADD_R0_R2_MEM = 79;
	public static final int ADD_R1_R2_MEM = 81;
	public static final int ADD_R2_R2_MEM = 83;
	public static final int ADD_R3_R2_MEM = 85;
	public static final int ADD_R4_R2_MEM = 87;

	public static final int ADD_R0_R3_MEM = 89;
	public static final int ADD_R1_R3_MEM = 91;
	public static final int ADD_R2_R3_MEM = 93;
	public static final int ADD_R3_R3_MEM = 95;
	public static final int ADD_R4_R3_MEM = 97;
	
	public static final int ADD_R0_R4_MEM = 99;
	public static final int ADD_R1_R4_MEM = 101;
	public static final int ADD_R2_R4_MEM = 103;
	public static final int ADD_R3_R4_MEM = 105;
	public static final int ADD_R4_R4_MEM = 107;
	
	public static final int ADD_R0_MEM_R0 = 109;
	public static final int ADD_R1_MEM_R0 = 111;
	public static final int ADD_R2_MEM_R0 = 113;
	public static final int ADD_R3_MEM_R0 = 115;
	public static final int ADD_R4_MEM_R0 = 117;
	
	public static final int ADD_R0_MEM_R1 = 119;
	public static final int ADD_R1_MEM_R1 = 121;
	public static final int ADD_R2_MEM_R1 = 123;
	public static final int ADD_R3_MEM_R1 = 125;
	public static final int ADD_R4_MEM_R1 = 127;
	
	public static final int ADD_R0_MEM_R2 = 129;
	public static final int ADD_R1_MEM_R2 = 131;
	public static final int ADD_R2_MEM_R2 = 133;
	public static final int ADD_R3_MEM_R2 = 135;
	public static final int ADD_R4_MEM_R2 = 137;

	public static final int ADD_R0_MEM_R3 = 139;
	public static final int ADD_R1_MEM_R3 = 141;
	public static final int ADD_R2_MEM_R3 = 143;
	public static final int ADD_R3_MEM_R3 = 145;
	public static final int ADD_R4_MEM_R3 = 147;
	
	public static final int ADD_R0_MEM_R4 = 149;
	public static final int ADD_R1_MEM_R4 = 151;
	public static final int ADD_R2_MEM_R4 = 153;
	public static final int ADD_R3_MEM_R4 = 155;
	public static final int ADD_R4_MEM_R4 = 157;
	
	private static final int ADD_RI_MEM_RJ_MEM = 159;
	public static final int ADD_R0_MEM_R0_MEM = 162;
	public static final int ADD_R1_MEM_R0_MEM = 164;
	public static final int ADD_R2_MEM_R0_MEM = 166;
	public static final int ADD_R3_MEM_R0_MEM = 168;
	public static final int ADD_R4_MEM_R0_MEM = 170;
	
	public static final int ADD_R0_MEM_R1_MEM = 172;
	public static final int ADD_R1_MEM_R1_MEM = 174;
	public static final int ADD_R2_MEM_R1_MEM = 176;
	public static final int ADD_R3_MEM_R1_MEM = 178;
	public static final int ADD_R4_MEM_R1_MEM = 180;
	
	public static final int ADD_R0_MEM_R2_MEM = 182;
	public static final int ADD_R1_MEM_R2_MEM = 184;
	public static final int ADD_R2_MEM_R2_MEM = 186;
	public static final int ADD_R3_MEM_R2_MEM = 188;
	public static final int ADD_R4_MEM_R2_MEM = 190;

	public static final int ADD_R0_MEM_R3_MEM = 192;
	public static final int ADD_R1_MEM_R3_MEM = 194;
	public static final int ADD_R2_MEM_R3_MEM = 196;
	public static final int ADD_R3_MEM_R3_MEM = 198;
	public static final int ADD_R4_MEM_R3_MEM = 200;
	
	public static final int ADD_R0_MEM_R4_MEM = 202;
	public static final int ADD_R1_MEM_R4_MEM = 204;
	public static final int ADD_R2_MEM_R4_MEM = 206;
	public static final int ADD_R3_MEM_R4_MEM = 208;
	public static final int ADD_R4_MEM_R4_MEM = 210;


	
	List<Microinstrucao> microinstrucoes;
	
	private int cont;
	
	public MemoriaDeControle() {
		microinstrucoes = new ArrayList<Microinstrucao>();
		cont = 0;
		
		fimDePrograma();
		
		decodificaInstrucao();
		
		incrementaPC();
		
		buscaProximaInstrucao();
		
		addConstRi(R0);		addConstRi(R1);		addConstRi(R2);
							addConstRi(R3);		addConstRi(R4);

		addConstRiMem();		addConstRiMem(R0);		addConstRiMem(R1);
		addConstRiMem(R2);		addConstRiMem(R3);		addConstRiMem(R4);
		
		addRiRj(R0, R0);addRiRj(R1, R0);addRiRj(R2, R0);addRiRj(R3, R0);addRiRj(R4, R0);
		addRiRj(R0, R1);addRiRj(R1, R1);addRiRj(R2, R1);addRiRj(R3, R1);addRiRj(R4, R1);
		addRiRj(R0, R2);addRiRj(R1, R2);addRiRj(R2, R2);addRiRj(R3, R2);addRiRj(R4, R2);
		addRiRj(R0, R3);addRiRj(R1, R3);addRiRj(R2, R3);addRiRj(R3, R3);addRiRj(R4, R3);
		addRiRj(R0, R4);addRiRj(R1, R4);addRiRj(R2, R4);addRiRj(R3, R4);addRiRj(R4, R4);
		
		addRiRjMem();
		addRiRjMem(R0,R0);addRiRjMem(R1,R0);addRiRjMem(R2,R0);addRiRjMem(R3,R0);addRiRjMem(R4,R0);
		addRiRjMem(R0,R1);addRiRjMem(R1,R1);addRiRjMem(R2,R1);addRiRjMem(R3,R1);addRiRjMem(R4,R1);
		addRiRjMem(R0,R2);addRiRjMem(R1,R2);addRiRjMem(R2,R2);addRiRjMem(R3,R2);addRiRjMem(R4,R2);
		addRiRjMem(R0,R3);addRiRjMem(R1,R3);addRiRjMem(R2,R3);addRiRjMem(R3,R3);addRiRjMem(R4,R3);
		addRiRjMem(R0,R4);addRiRjMem(R1,R4);addRiRjMem(R2,R4);addRiRjMem(R3,R4);addRiRjMem(R4,R4);
		
		addRiMemRj(R0,R0);addRiMemRj(R1,R0);addRiMemRj(R2,R0);addRiMemRj(R3,R0);addRiMemRj(R4,R0);
		addRiMemRj(R0,R1);addRiMemRj(R1,R1);addRiMemRj(R2,R1);addRiMemRj(R3,R1);addRiMemRj(R4,R1);
		addRiMemRj(R0,R2);addRiMemRj(R1,R2);addRiMemRj(R2,R2);addRiMemRj(R3,R2);addRiMemRj(R4,R2);
		addRiMemRj(R0,R3);addRiMemRj(R1,R3);addRiMemRj(R2,R3);addRiMemRj(R3,R3);addRiMemRj(R4,R3);
		addRiMemRj(R0,R4);addRiMemRj(R1,R4);addRiMemRj(R2,R4);addRiMemRj(R3,R4);addRiMemRj(R4,R4);
		
		addRiMemRjMem();
		addRiMemRjMem(R0,R0);addRiMemRjMem(R1,R0);addRiMemRjMem(R2,R0);addRiMemRjMem(R3,R0);addRiMemRjMem(R4,R0);
		addRiMemRjMem(R0,R1);addRiMemRjMem(R1,R1);addRiMemRjMem(R2,R1);addRiMemRjMem(R3,R1);addRiMemRjMem(R4,R1);
		addRiMemRjMem(R0,R2);addRiMemRjMem(R1,R2);addRiMemRjMem(R2,R2);addRiMemRjMem(R3,R2);addRiMemRjMem(R4,R2);
		addRiMemRjMem(R0,R3);addRiMemRjMem(R1,R3);addRiMemRjMem(R2,R3);addRiMemRjMem(R3,R3);addRiMemRjMem(R4,R3);
		addRiMemRjMem(R0,R4);addRiMemRjMem(R1,R4);addRiMemRjMem(R2,R4);addRiMemRjMem(R3,R4);addRiMemRjMem(R4,R4);
	}
	
	
	
	private void addRiMemRjMem(int Ri, int Rj) {
		microinstrucoes.add(new Microinstrucao(++cont, 
				muxa(Rj) | ULA_A | RT));
		microinstrucoes.add(new Microinstrucao(ADD_RI_MEM_RJ_MEM, 
				muxa(Ri) | ULA_A | REND | MEM_R)); cont++;
	}
	
	
	private void addRiMemRjMem() {
		microinstrucoes.add(new Microinstrucao(++cont, 
				MUX_MEM | RDADO));
		microinstrucoes.add(new Microinstrucao(++cont, 
				MUXA_RT | ULA_A | REND | MEM_R));
		microinstrucoes.add(new Microinstrucao(INCREMENTA_PC, 
				MUXA_RDADO | MUXB_RT | ULA_ADD_A_B | MUX_BARRAMENTO | RDADO | MEM_W));
		cont++;
	}
	
	
	private void addRiMemRj(int Ri, int Rj) {
		microinstrucoes.add(new Microinstrucao(++cont, 
				muxa(Ri) | ULA_A | REND | MEM_R));
		microinstrucoes.add(new Microinstrucao(INCREMENTA_PC, 
				MUXA_RDADO | muxb(Rj) | ULA_ADD_A_B | Rj)); cont++;
	}
	
	
	private void addRiRjMem(int Ri, int Rj) {
		microinstrucoes.add(new Microinstrucao(++cont, 
				muxa(Ri) | ULA_A | RT));
		microinstrucoes.add(new Microinstrucao(ADD_RI_RJ_MEM, 
				muxa(Rj) | ULA_A | REND)); cont++;
	}
	
	
	private void addRiRjMem() {
		microinstrucoes.add(new Microinstrucao(++cont, 
				MUX_MEM | RDADO));
		microinstrucoes.add(new Microinstrucao(INCREMENTA_PC, 
				MUXA_RDADO | MUXB_RT | ULA_ADD_A_B | MUX_BARRAMENTO | RDADO | MEM_W));
		cont++;
	}
	

	private void addRiRj(int Ri, int Rj) {
		microinstrucoes.add(new Microinstrucao(INCREMENTA_PC, 
				muxa(Ri) | muxb(Rj) | ULA_ADD_A_B | Rj)); cont++;
	}
	

	private void addConstRiMem(int Ri) {
		microinstrucoes.add(new Microinstrucao(ADD_CONST_RI_MEM, 
				muxa(Ri) | ULA_A | RT)); cont++;
	}
	
	
	private void addConstRiMem() {
		microinstrucoes.add(new Microinstrucao(++cont, 		//busca operando
				MUXA_PC | ULA_INC_A | PC | REND | MEM_R));	//constante imediata
		microinstrucoes.add(new Microinstrucao(++cont, 		//na posicao
				MUX_MEM | RDADO));							//seguinte de memoria
		microinstrucoes.add(new Microinstrucao(++cont, 
				MUXA_RT | ULA_A | REND));
		microinstrucoes.add(new Microinstrucao(++cont, 
				MUXA_RDADO | ULA_A | RT | MEM_R));
		microinstrucoes.add(new Microinstrucao(++cont, 
				MUX_MEM | RDADO));
		microinstrucoes.add(new Microinstrucao(INCREMENTA_PC, 
				MUXA_RDADO | MUXB_RT | ULA_ADD_A_B | MUX_BARRAMENTO | RDADO | MEM_W));
		cont++;
	}
	
	
	private void addConstRi(int Ri) {
		microinstrucoes.add(new Microinstrucao(++cont, 		//busca operando
				MUXA_PC | ULA_INC_A | PC | REND | MEM_R));	//constante imediata
		microinstrucoes.add(new Microinstrucao(++cont, 		//na posicao
				MUX_MEM | RDADO));							//seguinte de memoria
		microinstrucoes.add(new Microinstrucao(INCREMENTA_PC, 
				MUXA_RDADO | muxb(Ri) | ULA_A_AND_B | Ri)); cont++;
	}


	private void buscaProximaInstrucao() {
		//comeca na posicao 3
		microinstrucoes.add(new Microinstrucao(++cont, 
				MUXA_PC | REND | MEM_R)); 					//apontam para
		microinstrucoes.add(new Microinstrucao(++cont, 
				MUX_MEM | RDADO));	 						//proxima micro
		microinstrucoes.add(new Microinstrucao(DECODIFICA_INSTRUCAO, 
				MUXA_RDADO | IR)); cont++;
	}

	private void incrementaPC() {
		//posicao 2
		microinstrucoes.add(new Microinstrucao(BUSCA_INSTRUCAO, 
				MUXA_PC | PC | ULA_INC_A)); cont++;
	}	
	
	private void decodificaInstrucao() {
		//posicao 1
		microinstrucoes.add(new Microinstrucao(0, 0)); //endereco e controle = lixo (nao importam)
		cont++;
	}
	
	private void fimDePrograma() {
		//posicao 0
		microinstrucoes.add(new Microinstrucao(FIM_DE_PROGRAMA, 0)); //controle = nao faz nada
		cont++;
	}

	public Microinstrucao getMicroinstrucao(Integer endereco) {
		return microinstrucoes.get(endereco);
	}
	
	
	private int muxa(int Ri) {
		switch (Ri) {
		case RDADO:
			return MUXA_RDADO;
		case R0:
			return MUXA_R0;
		case R1:
			return MUXA_R1;
		case R2:
			return MUXA_R2;
		case R3:
			return MUXA_R3;
		case R4:
			return MUXA_R4;
		default:
			return MUXA_RT;
		}
	}
	
	
	private int muxb(int Ri) {
		switch (Ri) {
		case RDADO:
			return MUXA_RDADO;
		case R0:
			return MUXB_R0;
		case R1:
			return MUXB_R1;
		case R2:
			return MUXB_R2;
		case R3:
			return MUXB_R3;
		case R4:
			return MUXB_R4;
		default:
			return MUXB_RT;
		}
	}
	
}
